/* * Demoiselle Framework * Copyright (C) 2010 SERPRO * ---------------------------------------------------------------------------- * This file is part of Demoiselle Framework. * * Demoiselle Framework is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License version 3 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License version 3 * along with this program; if not, see <http://www.gnu.org/licenses/> * or write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301, USA. * ---------------------------------------------------------------------------- * Este arquivo é parte do Framework Demoiselle. * * O Framework Demoiselle é um software livre; você pode redistribuí-lo e/ou * modificá-lo dentro dos termos da GNU LGPL versão 3 como publicada pela Fundação * do Software Livre (FSF). * * Este programa é distribuído na esperança que possa ser útil, mas SEM NENHUMA * GARANTIA; sem uma garantia implícita de ADEQUAÇÃO a qualquer MERCADO ou * APLICAÇÃO EM PARTICULAR. Veja a Licença Pública Geral GNU/LGPL em português * para maiores detalhes. * * Você deve ter recebido uma cópia da GNU LGPL versão 3, sob o título * "LICENCA.txt", junto com esse programa. Se não, acesse <http://www.gnu.org/licenses/> * ou escreva para a Fundação do Software Livre (FSF) Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA. */ package br.gov.frameworkdemoiselle.template.contrib; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.persistence.Query; import javax.persistence.TransactionRequiredException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import br.gov.frameworkdemoiselle.DemoiselleException; import br.gov.frameworkdemoiselle.annotation.Name; import br.gov.frameworkdemoiselle.configuration.Configuration; import br.gov.frameworkdemoiselle.enumeration.contrib.Comparison; import br.gov.frameworkdemoiselle.query.contrib.QueryConfig; import br.gov.frameworkdemoiselle.query.contrib.QueryContext; import br.gov.frameworkdemoiselle.template.Crud; import br.gov.frameworkdemoiselle.transaction.Transactional; import br.gov.frameworkdemoiselle.util.Reflections; import br.gov.frameworkdemoiselle.util.ResourceBundle; /** * JPA specific implementation for Crud interface. * * @param <T> * bean object type * @param <I> * bean id type * @author SERPRO * @see Crud */ public class JPACrud<T, I> extends br.gov.frameworkdemoiselle.template.JPACrud<T, I> { private static final long serialVersionUID = 1L; @Inject private Instance<QueryContext> queryContext; private QueryConfig<T> queryConfig; @Inject @Name("demoiselle-jpa-bundle") private Instance<ResourceBundle> bundle; protected CriteriaBuilder cBuilder = null; protected Root<T> cRoot = null; private Class<T> beanClass; protected Class<T> getBeanClass() { if (this.beanClass == null) { this.beanClass = Reflections.getGenericTypeArgument(this.getClass(), 0); } return this.beanClass; } protected CriteriaQuery<T> getCriteria() { this.cBuilder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<T> criteria = this.cBuilder.createQuery(getBeanClass()); this.cRoot = criteria.from(getBeanClass()); return criteria; } protected <C> CriteriaQuery<C> getCriteria(Class<C> clazz) { this.cBuilder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<C> criteria = this.cBuilder.createQuery(clazz); this.cRoot = criteria.from(getBeanClass()); return criteria; } protected CriteriaBuilder getCriteriaBuilder() { if (this.cBuilder == null) throw new RuntimeException("Criteria is not initialized. Please use getCriteria() first."); return this.cBuilder; } protected Root<T> getCriteriaRoot() { if (this.cRoot == null) throw new RuntimeException("Criteria is not initialized. Please use getCriteria() first."); return this.cRoot; } protected QueryConfig<T> getQueryConfig() { if (queryConfig == null) { queryConfig = queryContext.get().getQueryConfig(getBeanClass()); } return queryConfig; } protected Query createQuery(final String ql) { return getEntityManager().createQuery(ql); } protected void handleException(Throwable cause) throws Throwable { if (cause instanceof TransactionRequiredException) { String message = bundle.get().getString("no-transaction-active", "frameworkdemoiselle.transaction.class", Configuration.DEFAULT_RESOURCE); throw new DemoiselleException(message, cause); } else { throw cause; } } @Override @Transactional public void insert(final T entity) { getEntityManager().persist(entity); } @Override @Transactional public void delete(final I id) { T entity = getEntityManager().getReference(getBeanClass(), id); getEntityManager().remove(entity); } @Override @Transactional public void update(final T entity) { getEntityManager().merge(entity); } @Override public T load(final I id) { return getEntityManager().find(getBeanClass(), id); } protected Order[] getOrder() { List<Order> orderList = new ArrayList<Order>(); for (String sortAtrr : queryConfig.getSorting()) if (sortAtrr != null) if (queryConfig.isSortOrder()) orderList.add(this.cBuilder.asc(getAttributeExpression(sortAtrr))); else orderList.add(this.cBuilder.desc(getAttributeExpression(sortAtrr))); return orderList.toArray(new Order[] {}); } protected String getComparison(String entityAttrValue) { if (entityAttrValue == null) return ""; else if (queryConfig.getFilterComparison() == Comparison.EQUALS) return entityAttrValue; else if (queryConfig.getFilterComparison() == Comparison.CONTAINS) return "%" + entityAttrValue + "%"; else if (queryConfig.getFilterComparison() == Comparison.STARTSWITH) return entityAttrValue + "%"; else return "%" + entityAttrValue; } protected Predicate getPredicateForString(Expression<String> attr, String value) { if (queryConfig.isFilterCaseInsensitive()) { attr = this.cBuilder.lower(attr); value = value.toLowerCase(); } if (queryConfig.isFilterLogicNegation()) return this.cBuilder.notLike(attr, getComparison(value)); else return this.cBuilder.like(attr, getComparison(value)); } protected Predicate getPredicate(Expression<String> attr, Object value) { if (queryConfig.isFilterLogicNegation()) return this.cBuilder.notEqual(attr, value); else return this.cBuilder.equal(attr, value); } protected Expression<String> getAttributeExpression(String attributeName) { // TODO: fix GabiMode making generic to support any navigation level // GambiModeOn to support 4 level entity navigation String[] attrNameList = attributeName.split("\\."); if (attrNameList.length == 4) return this.cRoot.get(attrNameList[0]).get(attrNameList[1]).get(attrNameList[2]).get(attrNameList[3]); else if (attrNameList.length == 3) return this.cRoot.get(attrNameList[0]).get(attrNameList[1]).get(attrNameList[2]); else if (attrNameList.length == 2) return this.cRoot.get(attrNameList[0]).get(attrNameList[1]); else return this.cRoot.get(attributeName); // GambiModeOff } protected Predicate getWhere() { if (queryConfig.getFilter() == null || queryConfig.getFilter().isEmpty()) return null; List<Predicate> predicates = new ArrayList<Predicate>(); for (Map.Entry<String, Object> entry : queryConfig.getFilter().entrySet()) { if (entry.getKey() == null || entry.getValue() == null) continue; Object[] values; if (entry.getValue().getClass().isArray()) { values = (Object[]) entry.getValue(); if (values.length == 0) continue; } else values = new Object[] { entry.getValue() }; for (Object value : values) { if (value == null) continue; if (value instanceof String && queryConfig.getFilterComparison() != Comparison.EQUALS) predicates.add(getPredicateForString(getAttributeExpression(entry.getKey()), (String) value)); else predicates.add(getPredicate(getAttributeExpression(entry.getKey()), value)); } } if (predicates.size() == 0) return null; if (queryConfig.isFilterLogicConjunction()) return this.cBuilder.and(predicates.toArray(new Predicate[] {})); else return this.cBuilder.or(predicates.toArray(new Predicate[] {})); } @Override public List<T> findAll() { CriteriaQuery<T> criteria = getCriteria(); getQueryConfig(); if (queryConfig != null) { Predicate predicate = getWhere(); if (predicate != null) criteria.where(predicate); if (queryConfig.getSorting() != null && queryConfig.getSorting().length != 0) criteria.orderBy(getOrder()); } TypedQuery<T> query = getEntityManager().createQuery(criteria); if (queryConfig != null) { if (queryConfig.isPaginated()) { query.setFirstResult(queryConfig.getFirstResult()); queryConfig.setTotalResults(countAll()); } if (queryConfig.getMaxResults() > 0) query.setMaxResults(queryConfig.getMaxResults()); } return query.getResultList(); } /** * Retrieves the number of persisted objects for the current class type. * * @return the row count */ protected int countAll() { CriteriaQuery<Long> criteria = this.cBuilder.createQuery(Long.class); criteria.from(getBeanClass()); criteria.select(this.cBuilder.count(this.cRoot)); Predicate predicate = getWhere(); if (predicate != null) criteria.where(predicate); return getEntityManager().createQuery(criteria).getSingleResult().intValue(); } /** * Retrieves the number of persisted objects type by hql and parameters * (name and value). Examples: * * countAll("from User u where u.city = :city order by u.name", "city", * "New York") * * Starts with "from Entity e" is required for create "select count(e)"; * * @return the row count */ protected int countAll(String hql, Object... parameters) { Query query = createQuery("select count(" + hql.split(" ")[2] + ") " + hql); setQueryParameters(query, parameters); return ((Long) query.getSingleResult()).intValue(); } protected Query createQuery(String ql, Object... parameters) { Query query = createQuery(ql); setQueryParameters(query, parameters); setQueryPagination(query, ql, parameters); return query; } private void setQueryParameters(Query query, Object... parameters) { if (parameters.length % 2 != 0) throw new IllegalArgumentException(); for (int i = 0; i < parameters.length; i++) { query.setParameter((String) parameters[i], parameters[i + 1]); i++; } } private void setQueryPagination(Query query, String ql, Object... parameters) { getQueryConfig(); if (queryConfig != null) { queryConfig.setTotalResults(countAll(ql, parameters)); if (queryConfig.getMaxResults() > 0) { query.setFirstResult(queryConfig.getFirstResult()); query.setMaxResults(queryConfig.getMaxResults()); } } } }